<!--
  列表的统一封装，包括列表的查询、删除、排序，以及表单的新增、编辑、查看等功能

  使用到的库：vxe-table, element-ui
  相关文档地址：
      vxe-table：https://xuliangzhan_admin.gitee.io/vxe-table/#/
      element-ui：https://element.eleme.cn/#/zh-CN/component/installation

  示例：

    示例1：
    <qm-table :data="query" keyProp="typeId" :formOptions="{ labelWidth: '120px' }" :menuOptions="{ width: 200 }" :rules="rules">

      <qm-table-column field="name" sortable>字典类型名称</qm-table-column>
      <qm-table-column field="code" sortable>字典类型编码</qm-table-column>

      <template v-slot:toolbar>
        <el-button icon="el-icon-circle-plus-outline">测试</el-button>
      </template>
      <template v-slot:moreBtn="{ row }">
        <el-button icon="el-icon-more-outline" circle @click="show(row)"></el-button>
      </template>
      <template v-slot:form="{ model }">
        <el-form-item label="字典类型名称" prop="model.name">
          <el-input v-model="model.name"></el-input>
        </el-form-item>
        <el-form-item label="字典类型编码" prop="model.code">
          <el-input v-model="model.code"></el-input>
        </el-form-item>
        <el-form-item label="备注" prop="model.memo">
          <el-input type="textarea" v-model="model.memo"></el-input>
        </el-form-item>
      </template>
    </qm-table>

    示例2：
    <qm-table :urls="urls" keyProp="id" :beforeQuery="beforeQuery" :afterQuery="afterQuery">
      <qm-table-column field="name" sortable>中文名称</qm-table-column>
      <qm-table-column field="code" sortable>编码</qm-table-column>
      <qm-table-column
        field="isEncode"
        fieldtype="enum"
        :enumOptions="[
          { key: '是', value: true },
          { key: '否', value: false },
        ]"
        sortable
      >是否加密</qm-table-column>
      <qm-table-column field="isEncode" fieldtype="yesno" sortable>是否加密</qm-table-column>
      <qm-table-column field="value" sortable>参数值</qm-table-column>
      <qm-table-column field="category" sortable>分类</qm-table-column>
      <qm-table-column field="isEnabled" fieldtype="enabledisable" sortable>是否启用</qm-table-column>
      <qm-table-column field="updatedDate" fieldtype="datetime" sortable>更新时间</qm-table-column>

    </qm-table>


  说明：
    data属性：分页对象，取值参考：
      {
        data: [],
        draw: 1,
        startIndex: 1,
        pageSize: 10,
        pages: 2,
        recordsFiltered: 20,
        recordsTotal: 20,
      },
    urls属性：数据请求的接口，指定urls的情况下会忽视data属性
      {
        query: { url: "/admin/sysParameter/query", method: "post" },  // 列表数据接口
        save: "/admin/sysParameter/save",                             // 实体保存接口
        delete: "/admin/sysParameter/delete",                         // 实体删除接口
        entity: "/admin/sysParameter/getEntity",                      // 获取实体接口
      }

    keyProp属性：主键属性，指该列表对应的实体的主键属性，如：userId，typeId等，请尽量指定主键属性
    rules属性: el表单校验规则，取值参考：
      {
        model: {
          name: [
            { required: true, message: "请输入字典类型名称", trigger: "blur" },
          ],
          code: [
            { required: true, message: "请输入字典类型编码", trigger: "blur" },
          ],
        }
      },

    其他属性请查看组件内部注释或相关文档
-->
<template>
  <div class="qm-table">
    <vxe-toolbar v-if="showToolbar" v-bind="finalToolbarOptions">
      <template v-slot:buttons>
        <el-button
          icon="el-icon-circle-plus-outline"
          v-if="finalShowAdd"
          @click="addNew"
          type="primary"
          size="small"
        >
          {{ textMap.add || "新增" }}
        </el-button>
        <el-button
          icon="el-icon-download"
          v-if="showExport"
          @click="excelExport('.xlsx')"
          type="primary"
          size="small"
        >
          {{ textMap.export || "导出Excel" }}
        </el-button>
        <el-button
          icon="el-icon-download"
          v-if="showSeniorExport"
          @click="seniorExport"
          type="primary"
          size="small"
        >
          {{ textMap.seniorExport || "高级导出" }}
        </el-button>
        <el-button
          icon="el-icon-remove-outline"
          v-if="showCheckbox && finalShowDelete"
          type="danger"
          @click="deleteSelected"
          size="small"
        >
          {{ textMap.delSelected || "删除" }}
        </el-button>

        <slot name="toolbar"></slot>
      </template>
    </vxe-toolbar>

    <vxe-table
      v-bind="finalOptions"
      v-on="finalTableEvents"
      ref="xTable"
      :data="data.data"
      :loading="tableLoading"
      @sort-change="sortChange"
      @cell-click="({ row,column })=>{excuteEvent('cell-click',{ row,column })}"
      class="qm-table-vxe-table"
    >
      <vxe-column
        v-if="showRadio"
        type="radio"
        width="60"
        align="center"
        fixed="left"
      >
        <template #header>
          <el-button
            type="text"
            @click="$refs.xTable.clearRadioRow()"
            style="color: #606266;"
            >取消</el-button
          >
        </template>
      </vxe-column>
      <vxe-column
        v-if="showCheckbox"
        align="center"
        type="checkbox"
        width="60"
        fixed="left"
      ></vxe-column>

      <vxe-column
        v-if="showOrder"
        type="seq"
        align="center"
        :title="textMap.order || '序号'"
        width="60"
        fixed="left"
      ></vxe-column>
      <slot></slot>

      <vxe-column v-if="showMenu" v-bind="finalMenuOptions">
        <template #header>
          <slot name="moreHeader">{{finalMenuOptions.title}}</slot>
        </template>
        <template v-slot="{ row }">
          <template v-if="menuType === 'icon'">
            <el-button
              v-if="finalShowView && hasKeyValue(row) && finalOperMenuRenderFuncMap.view(row)"
              icon="el-icon-view"
              circle
              @click="viewInfo(row)"
            ></el-button>
            <el-button
              v-if="finalShowEdit && hasKeyValue(row) && finalOperMenuRenderFuncMap.edit(row)"
              type="primary"
              icon="el-icon-edit"
              circle
              @click="showEditModal(row)"
            ></el-button>
            <el-button
              v-if="finalShowDelete && hasKeyValue(row) && finalOperMenuRenderFuncMap.remove(row)"
              type="danger"
              icon="el-icon-delete"
              circle
              @click="remove(row)"
            ></el-button>
          </template>
          <template v-else-if="menuType === 'text'">
            <el-button
              type="text"
              v-if="finalShowView && hasKeyValue(row) && finalOperMenuRenderFuncMap.view(row)"
              @click="viewInfo(row)"
              >{{ textMap.view || "查看" }}</el-button
            >
            <el-button
              type="text"
              v-if="finalShowEdit && hasKeyValue(row) && finalOperMenuRenderFuncMap.edit(row)"
              @click="showEditModal(row)"
              >{{ textMap.edit || "编辑" }}</el-button
            >
            <el-button
              type="text"
              v-if="finalShowDelete && hasKeyValue(row) && finalOperMenuRenderFuncMap.remove(row)"
              @click="remove(row)"
              >{{ textMap.remove || "删除" }}</el-button
            >
          </template>
          <template v-else>
            <el-button
              v-if="finalShowView && hasKeyValue(row) && finalOperMenuRenderFuncMap.view(row)"
              icon="el-icon-view"
              type="text"
              @click="viewInfo(row)"
              >{{ textMap.view || "查看" }}</el-button
            >
            <el-button
              v-if="finalShowEdit && hasKeyValue(row) && finalOperMenuRenderFuncMap.edit(row)"
              icon="el-icon-edit"
              type="text"
              @click="showEditModal(row)"
              >{{ textMap.edit || "编辑" }}</el-button
            >
            <el-button
              v-if="finalShowDelete && hasKeyValue(row) && finalOperMenuRenderFuncMap.remove(row)"
              icon="el-icon-delete"
              type="text"
              @click="remove(row)"
              >{{ textMap.remove || "删除" }}</el-button
            >
          </template>
          <slot name="moreBtn" v-bind:row="row"></slot>
        </template>
      </vxe-column>
    </vxe-table>

    <template v-if="showPager">
      <el-pagination
        v-if="elPager"
        class="qm-table-el-pager"
        @size-change="handleElPageSizeChange"
        @current-change="handleElPageChange"
        :current-page="data.startIndex"
        v-bind="finalPagerOptions"
        :page-size="data.pageSize"
        :total="data.recordsTotal"
        :style="{'text-align':pagerAlign}"
      ></el-pagination>

      <vxe-pager
        v-else
        :current-page="data.startIndex"
        :page-size="data.pageSize"
        :total="data.recordsTotal"
        v-bind="finalPagerOptions"
        @page-change="handlePageChange"
        :style="{'text-align':pagerAlign}"
      ></vxe-pager>
    </template>

    <vxe-modal
      class="qm-table-form-modal"
      ref="xModal"
      v-model="formModalVisible"
      v-bind="finalModalOptions"
      :loading="formModalLoading"
      :showFooter="showFormFooter"
      v-on="finalFormModalEvents"
    >
      <template v-slot v-if="useFormPage">
        <component
          ref="formPage"
          :is="formPath"
          v-if="formPath"
          v-bind:viewModel="viewModel"
          v-bind="viewModel"
          v-bind:formData="viewModel.models.model"
          v-bind:model="viewModel.models.model"
          v-bind:disabled="formDisabled"
        ></component>
      </template>
      <template v-slot v-else-if="!isCustomForm">
        <el-form
          class="qm-table-form"
          :model="viewModel.models"
          v-bind="finalFormOptions"
          :disabled="formDisabled"
          :rules="rules"
          :ref="formRefs"
        >
          <slot
            name="form"
            v-bind:viewModel="viewModel"
            v-bind="viewModel"
            v-bind:formData="viewModel.models.model"
            v-bind:model="viewModel.models.model"
            v-bind:disabled="formDisabled"
          ></slot>
        </el-form>
      </template>
      <template v-slot v-else>
        <slot
          v-if="!formModalLoading"
          name="form"
          v-bind:viewModel="viewModel"
          v-bind="viewModel"
          v-bind:formData="viewModel.models.model"
          v-bind:model="viewModel.models.model"
          v-bind:disabled="formDisabled"
        ></slot>
      </template>
      <template #footer v-if="showFormFooter">
        <slot name="formToolbar" v-bind:vm="this">
          <el-row
            class="qm-table-form-row"
            v-if="
              keyProp && ['create', 'edit'].includes(getParent().pageAction)
            "
          >
            <el-button
              type="primary"
              :loading="saveLoading"
              @click="submitForm"
              icon="el-icon-circle-check"
              >{{ textMap.save || "保 存" }}</el-button
            >
            <!-- <el-button @click="resetForm" >重 置</el-button> -->
            <el-button
              @click="formModalVisible = false"
              icon="el-icon-circle-close"
              >{{ textMap.cancel || "取 消" }}</el-button
            >
          </el-row>
        </slot>
      </template>
    </vxe-modal>

    <qm-export-modal
      v-if="showSeniorExport"
      v-model="exportModalVisible"
      @export-modal-close="exportModalVisible = false"
      :exportConfig="finalExportConfig"
      :seniorExportUrl="seniorExportUrl"
      :modalOptions="finalExportModalOptions"
    >
    </qm-export-modal>
  </div>
</template>

<script>
import _ from "lodash";
import { checkPermi } from "@/utils/permission";
import request from "@/utils/request";
import QmExportModal from "@/libs/components/qm-export-modal";

export default {
  name: "qm-table",
  props: {
    // 一些按钮的默认文字
    textMap: {
      type: Object,
      default: () => {
        return {
          add: "新增",
          view: "查看",
          edit: "编辑",
          remove: "删除",
          delSelected: "删除",
          export: "导出Excel",
          seniorExport: "高级导出",
          order: "序号",
          save: "保 存",
          cancel: "取 消"
        };
      }
    },
    /**
     * 操作表单弹出框默认标题
     */
    operTitleMap: {
      type: Object,
      default: () => {
        return {
          create: '新增',
          edit: '编辑',
          view: '详情'
        }
      }
    },
    /**
     * 操作菜单渲染方法：
     */
    operMenuRenderFuncMap: {
      type: Object,
      default: () => {
        return {
          remove: (row) => true,
          edit: (row) => true,
          view: (row) => true
        }
      }
    },
    // 是否显示序号
    showOrder: {
      type: Boolean,
      default: true
    },
    // 是否显示单选框
    showRadio: {
      type: Boolean,
      default: false
    },
    // 是否显示复选框
    showCheckbox: {
      type: Boolean,
      default: true
    },
    // 是否显示操作列
    showMenu: {
      type: Boolean,
      default: true
    },
    // 操作列类型，纯图标 icon，纯文本 text，文本图标 icontext
    menuType: {
      type: String,
      default: "icon"
    },
    // 是否显示新增按钮
    showAdd: {
      type: Boolean,
      default: true
    },
    // 是否显示操作列查看按钮
    showView: {
      type: Boolean,
      default: true
    },
    // 是否显示操作列编辑按钮
    showEdit: {
      type: Boolean,
      default: true
    },
    // 是否显示操作列删除按钮
    showDelete: {
      type: Boolean,
      default: true
    },
    // 是否显示导出Excel按钮
    showExport: {
      type: Boolean,
      default: false
    },
    // 是否显示高级导出按钮
    showSeniorExport: {
      type: Boolean,
      default: false
    },
    // 是否显示工具栏
    showToolbar: {
      type: Boolean,
      default: true
    },
    // 是否显示分页
    showPager: {
      type: Boolean,
      default: true
    },
    /**
     * 是否启用el-pagination的分页组件，默认true，false 表示使用vxe-pager
     *
     * 注：嵌套的 el-dialog 或 vxe-modal 里面存在 vxe-pager 时，点击分页按钮可能会出现页面跳转(刷新)的问题。
     * 出现此问题，请检查vxe-pager组件是否内嵌于form表单内，如果是，只需要移出form表单即可。
     *
     * 原因：vxe-pager被el-form(form表单)包裹时，点击分页按钮会触发表单的action属性，从而导致页面跳转(刷新)的问题。
     */
    elPager: {
      type: Boolean,
      default: true
    },
    pagerAlign: {
      type: String,
      default: "left"
    },
    // 保存后刷新
    refreshAfterSave: {
      type: Boolean,
      default: true
    },
    // 列表刷新类型，取值list、list$1、list$2 等，与controller的this.urls.query的类型对应
    // 值为 none 表示不刷新
    refreshType: {
      type: String,
      default: "list"
    },
    // 主键属性，指该列表对应的实体的主键属性，如：userId，typeId等，请尽量指定主键属性
    // 双主键请用英文逗号分隔，如：userId,profileId
    keyProp: {
      type: String
    },
    // 是否是自定义表单内容，此时rules属性无效，需要自己指定表单的rules校验，
    // 保存和校验的方法如果用qm-table组件的，则还需要设置formRefs属性，否则保存和校验的方法还需要自己编写
    // 默认为false，不自定义，使用组件提供的表单，rules属性有效
    isCustomForm: {
      type: Boolean
    },
    // 操作列的一些属性配置，请参考vxe-table 3.x版本文档：https://xuliangzhan_admin.gitee.io/vxe-table/#/table-column/api
    menuOptions: {
      type: Object,
      default: () => {}
    },
    // 分页行的一些属性配置，请参考vxe-table 3.x版本文档：https://xuliangzhan_admin.gitee.io/vxe-table/#/pager/api
    pagerOptions: {
      type: Object,
      default: () => {}
    },
    // 表单模态框的一些属性配置，请参考vxe-table 3.x版本文档：https://xuliangzhan_admin.gitee.io/vxe-table/#/modal/api
    modalOptions: {
      type: Object,
      default: () => {}
    },
    // 表单的一些属性配置，请参考ElementUI 2.x版本文档：https://element.eleme.cn/#/zh-CN/component/form
    formOptions: {
      type: Object,
      default: () => {}
    },
    // 工具栏的一些属性配置，请参考vxe-table 3.x版本文档：https://xuliangzhan_admin.gitee.io/vxe-table/#/toolbar/api
    toolbarOptions: {
      type: Object,
      default: () => {}
    },
    // 表格的一些属性配置，请参考vxe-table 3.x版本文档：https://xuliangzhan_admin.gitee.io/vxe-table/#/table/api
    options: {
      type: Object,
      default: () => {}
    },
    // 表格的一些事件配置，请参考vxe-table 3.x版本文档：https://xuliangzhan_admin.gitee.io/vxe-table/#/table/api
    tableEvents: {
      type: Object,
      default: () => {}
    },
    // 表格表单模态框一些事件配置，请参考vxe-table 3.x版本文档：https://vxetable.cn/v3/#/modal/api
    formModalEvents: {
      type: Object,
      default: () => {}
    },
    // 表单的一些属性校验规则，支持一个表单内多个对象的校验，
    // 请参考ElementUI 2.x版本文档：https://element.eleme.cn/#/zh-CN/component/form
    rules: {
      type: Object,
      default: () => {}
    },
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // !!!!  该  属  性  废  弃  使  用  !!!
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // 该属性已过时，已通过provide / inject 来替代
    // vue 实例，指页面上继承controller的那个页面，默认不需要赋值，当在弹出框如 el-dialog 里使用qm-table组件时，
    // 很多方法都失效，此时需要指定parent属性，指定到页面上继承controller的那个页面的 vue 实例
    parent: {
      type: Object
    },
    // 列表显示的数据，取值为后台的分页对象PagerInfo，即列表页的query属性
    data: {
      type: Object,
      default: () => {
        return {
          data: [],
          draw: 1,
          startIndex: 1,
          pageSize: 10,
          pages: 1,
          recordsFiltered: 10,
          recordsTotal: 10
        };
      }
    },
    // 指定相关的url，包括列表、获取实体、保存、删除的接口url
    // 优先级高于 data，指定 url 的情况下 data 属性无效
    urls: {
      type: Object,
      default: () => {}
    },
    // 请求之前，带一个参数：params，仅对于指定urls的实例有效
    beforeQuery: {
      type: Function
    },
    // 请求之后，带两个参数：isSuccess、result，仅对于指定urls的实例有效
    afterQuery: {
      type: Function
    },
    // 获取实体之前，带三个参数：params, row, pageAction
    // row 表示列表的一条记录
    beforeGetEntity: {
      type: Function
    },
    // 表单的ref值，多个用英文逗号分隔，用于表单校验
    formRefs: {
      type: String,
      default: "qmGenForm"
    },
    // 是否显示表单的底部按钮
    showFormFooter: {
      type: Boolean,
      default: true
    },
    // 是否启用权限控制
    enablePermission: {
      type: Boolean,
      default: true
    },
    // 自行决定表单校验的逻辑，方法需返回Promise，且resolve的值为true或false，表示表单校验通过与否
    // 接收一个数组参数，表单的ref值数组
    formValidate: {
      type: Function
    },
    // 添加按钮的方法实现，将会覆盖默认的添加方法实现
    addFunc: {
      type: Function
    },
    // 编辑按钮的方法实现，将会覆盖默认的编辑方法实现，接收一个参数row，表示当前行
    editFunc: {
      type: Function
    },
    // 查看按钮的方法实现，将会覆盖默认的查看方法实现，接收一个参数row，表示当前行
    viewFunc: {
      type: Function
    },
    // 删除按钮的方法实现，将会覆盖默认的删除方法实现，接收一个参数rows，表示选中的行，
    // 如果是点击操作列的删除按钮，则rows只有一个元素
    removeFunc: {
      type: Function
    },
    // 列表刷新的方法实现，将会覆盖默认的列表刷新方法，接收一个参数params，一些请求参数
    refreshFunc: {
      type: Function
    },
    // 是否使用指定的页面来作为表单页
    useFormPage: {
      type: Boolean,
      default: false
    },
    // 指定的新增页、编辑页和查看页，不指定将统一用 create.vue 来作为表单页
    formPage: {
      type: Object,
      default: () => {
        return {
          // 参考示例
          // create: '/admin/sysUser/create',
          // edit: '/admin/sysUser/create',
          // view: '/admin/sysUser/create'
        };
      }
    },
    // 高级导出的配置
    exportConfig: {
      type: Object,
      default: () => {
        return {
          /**
          参考示例：
            filename：文件名，默认为"导出_20210915150452"，
            type：文件类型，默认为"xlsx"(暂时只支持xlsx)
            columns: 选择字段，必须。[{
              field: xxx,
              title: xxx
            }]
           */
        };
      }
    },
    // 高级导出接口地址
    seniorExportUrl: {
      type: String,
      required: false
    },
    // 导出模态框属性配置，请参考vxe-table 3.x版本文档：https://xuliangzhan_admin.gitee.io/vxe-table/#/modal/api
    exportModalOptions: {
      type: Object,
      default: () => {}
    },
    eventOptions:{
      type: Object,
      default: () => {
        return {};
      }
    }
  },
  inject: ["pageVue"],
  computed: {
    finalOperTitleMap() {
      return _.extend({
        create: '新增',
        edit: '编辑',
        view: '详情'
      }, this.operTitleMap)[this.getParent().pageAction] || '标题';
    },
    finalOptions() {
      return _.extend(
        {
          border: true,
          highlightHoverRow: true,
          highlightHoverColumn: true,
          autoResize: true,
          checkboxConfig: { highlight: true },
          sortConfig: {
            remote: true,
            trigger: "cell",
            orders: ["asc", "desc", null]
          }
        },
        this.options
      );
    },
    finalTableEvents() {
      return _.extend(
        {},
        this.tableEvents
      );
    },
    finalFormModalEvents() {
      return _.extend(
        {},
        this.formModalEvents
      );
    },
    finalMenuOptions() {
      return _.extend(
        {
          title: "操作",
          width: 150,
          fixed: "right"
        },
        this.menuOptions
      );
    },
    finalPagerOptions() {
      if (this.elPager) {
        return _.extend(
          {
            layout: "total, sizes, prev, pager, next, jumper",
            pageSizes: [5, 10, 20, 50, 100]
          },
          this.pagerOptions
        );
      }
      return _.extend(
        {
          layouts: [
            "Total",
            "Sizes",
            "PrevJump",
            "PrevPage",
            "JumpNumber",
            "NextPage",
            "NextJump",
            "FullJump"
          ],
          pageSizes: [5, 10, 20, 50, 100]
        },
        this.pagerOptions
      );
    },
    finalFormOptions() {
      return _.extend(
        {
          labelWidth: "100px"
        },
        this.formOptions
      );
    },
    finalModalOptions() {
      return _.extend(
        {
          width: 1024,
          resize: true,
          destroyOnClose: true,
          title: this.finalOperTitleMap,
          showZoom: true,
          escClosable: true
        },
        this.isCustomForm || this.useFormPage
          ? {
              height: 768
            }
          : {},
        this.modalOptions
      );
    },
    finalToolbarOptions() {
      return _.extend({}, this.toolbarOptions);
    },
    isRemoteList() {
      return !(_.isEmpty(this.urls) || _.isEmpty(this.urls.query));
    },
    finalShowAdd() {
      if (this.isRemoteList) {
        return this.showAdd && this.keyProp && this.urls.entity;
      }
      return (
        this.showAdd &&
        this.keyProp &&
        this.permissionCheck(this.getParent().getPermission("add"))
      );
    },
    finalShowView() {
      if (this.isRemoteList) {
        return this.showView && this.keyProp && this.urls.entity;
      }
      return (
        this.showView &&
        this.keyProp &&
        this.permissionCheck(this.getParent().getPermission("view"))
      );
    },
    finalShowEdit() {
      if (this.isRemoteList) {
        return this.showEdit && this.keyProp && this.urls.entity;
      }
      return (
        this.showEdit &&
        this.keyProp &&
        this.permissionCheck(this.getParent().getPermission("edit"))
      );
    },
    finalShowDelete() {
      if (this.isRemoteList) {
        return (
          this.showDelete &&
          this.keyProp &&
          (this.urls.delete || this.urls.remove)
        );
      }
      return (
        this.showDelete &&
        this.keyProp &&
        this.permissionCheck(this.getParent().getPermission("delete"))
      );
    },
    finalExportConfig() {
      return _.extend({}, { columns: this.exportColumns }, this.exportConfig);
    },
    finalExportModalOptions() {
      return _.extend({}, this.exportModalOptions);
    },
    finalOperMenuRenderFuncMap() {
      return _.extend({
        remove: (row) => true,
        edit: (row) => true,
        view: (row) => true
      }, this.operMenuRenderFuncMap);
    }
  },
  components: {
    QmExportModal
  },
  data() {
    return {
      formModalVisible: false,
      formModalLoading: false,
      viewModel: {
        search: {},
        controlData: {},
        models: {
          model: {}
        },
        defaults: {},
        enums: {},
        initMessage: null
      },
      primaryKey: {},
      tableLoading: false,
      saveLoading: false,
      formDisabled: false,
      formPath: "",
      exportModalVisible: false,
      exportColumns: []
    };
  },
  methods: {
    getFormPage() {
      if (!this.useFormPage) {
        return {};
      }
      if (_.isEmpty(this.formPage)) {
        let urls = this.getParent().urls;
        return urls && urls.create
          ? { create: urls.create, edit: urls.create, view: urls.create }
          : {};
      }
      return this.formPage;
    },
    computePage(pageAction) {
      let vpath = this.getFormPage()[pageAction];
      return resolve => require(["@/views" + vpath], resolve);
    },
    getParent() {
      if (this.pageVue) {
        return this.pageVue;
      }
      console.error(
        "该vue页面没有继承controller.js，qm-table组件的一些和表单相关的功能可能有异常！"
      );
      if (this.parent) {
        return this.parent;
      }
      return this.$parent;
    },
    hasKeyValue(row) {
      let hasValue = true;
      _.each(this.keyProp.split(","), key => {
        let k = key.trim();
        if (!row[k]) {
          hasValue = false;
        }
      });
      return hasValue;
    },
    permissionCheck(value) {
      if (!this.enablePermission || !value) {
        return true;
      }
      return checkPermi(_.isArray(value) ? value : [value]);
    },
    sortChange({ column, property, order }) {
      let params = {};
      if (order) {
        params = {
          order: {
            field: property,
            dir: order ? order : ""
          }
        };
      }
      this.refresh(params);
    },
    handleElPageSizeChange(pageSize) {
      let currentPage = this.data.startIndex;
      let params = {
        start: (currentPage - 1) * pageSize,
        length: pageSize
      };
      this.refresh(params);
    },
    handleElPageChange(currentPage) {
      let pageSize = this.data.pageSize;
      let params = {
        start: (currentPage - 1) * pageSize,
        length: pageSize
      };
      this.refresh(params);
    },
    handlePageChange({ currentPage, pageSize }) {
      let params = {
        start: (currentPage - 1) * pageSize,
        length: pageSize
      };
      this.refresh(params);
    },
    remove(row) {
      if (this.removeFunc) {
        return this.removeFunc([row]);
      }
      let postModel = this.getPrimaryKey(this.keyProp, row);

      let url = this.isRemoteList
        ? this.urls.remove || this.urls.delete
        : this.getParent().super.urls.delete || this.getParent().urls.delete;
      const lastPageDeleteRefreshParams = this.getLastPageDeleteRefreshParams(1);
      this.getParent()
        .remove(postModel, url)
        .then(() => {
          this.refresh(lastPageDeleteRefreshParams);
        })
        .catch(() => {});
    },
    deleteSelected() {
      let selectRecords = this.$refs["xTable"].getCheckboxRecords();
      if (this.removeFunc) {
        return this.removeFunc(selectRecords);
      }
      let postModels = [];
      if (selectRecords.length === 0) {
        this.msgError("请至少选中一项");
        return;
      }
      selectRecords.forEach(item => {
        let postModel = this.getPrimaryKey(this.keyProp, item);
        if (!_.isEmpty(postModel)) {
          postModels.push(postModel);
        }
      });
      let url = this.isRemoteList
        ? this.urls.remove || this.urls.delete
        : this.getParent().super.urls.delete || this.getParent().urls.delete;
      const lastPageDeleteRefreshParams = this.getLastPageDeleteRefreshParams(postModels.length);
      this.getParent()
        .remove(postModels, url)
        .then(() => {
          this.refresh(lastPageDeleteRefreshParams);
        })
        .catch(() => {});
    },
    getLastPageDeleteRefreshParams(removeCount) {
      let params = {};
      const { recordsTotal, startIndex, pageSize } = this.data;
      const condition =
        recordsTotal - removeCount <= (startIndex - 1) * pageSize &&
        recordsTotal != 0;
      if (condition) {
        params = {
          start: (startIndex - 2) * pageSize,
          length: pageSize,
        };
      }
      return params;
    },
    refresh(params = {}) {
      params = _.extend(
        {},
        {
          start: (this.data.startIndex - 1) * this.data.pageSize,
          length: this.data.pageSize
        },
        params
      );
      if (this.refreshFunc) {
        return this.refreshFunc(params);
      }
      if (this.isRemoteList) {
        this.fetchListData(params);
      } else {
        if (this.refreshType && this.refreshType !== "none") {
          this.tableLoading = true;
          this.getParent()
            .fetchData(params, this.refreshType, false)
            .then(r => {
              this.tableLoading = false;
            });
        }
      }
    },
    viewInfo(row) {
      if (this.viewFunc) {
        return this.viewFunc(row);
      }
      let params = this.getPrimaryKey(this.keyProp, row);
      params["pageAction"] = "view";
      let url = this.isRemoteList
        ? this.urls.entity
        : this.getParent().super.urls.entity || this.getParent().urls.entity;
      if (this.beforeGetEntity) {
        this.beforeGetEntity(params, row, "view");
      }
      this.formModalVisible = true;
      this.formModalLoading = true;
      this.getParent()
        .getEntity(params, url)
        .then(vm => {
          this.viewModel = vm;
          this.primaryKey = {};
          this.getParent().pageAction = "view";
          this.formPath = this.computePage("view");
          this.formModalLoading = false;
          this.$nextTick(() => {
            this.formDisabled = true;
          });
        })
        .catch(e => {
          this.msgError(e || "请求数据失败，请稍后再试");
        });
    },
    addNew() {
      if (this.addFunc) {
        return this.addFunc();
      }
      let url = this.isRemoteList
        ? this.urls.entity
        : this.getParent().super.urls.entity || this.getParent().urls.entity;
      if (this.beforeGetEntity) {
        this.beforeGetEntity({}, {}, "create");
      }
      this.formModalVisible = true;
      this.formModalLoading = true;
      this.getParent()
        .getEntity(undefined, url)
        .then(vm => {
          this.viewModel = vm;
          this.primaryKey = {};
          this.getParent().pageAction = "create";
          this.formPath = this.computePage("create");
          this.formModalLoading = false;
          this.$nextTick(() => {
            this.formDisabled = false;
          });
        })
        .catch(e => {
          this.msgError(e || "请求数据失败，请稍后再试");
        });
    },
    showEditModal(row) {
      if (this.editFunc) {
        return this.editFunc(row);
      }
      let params = this.getPrimaryKey(this.keyProp, row);
      params["pageAction"] = "edit";
      let url = this.isRemoteList
        ? this.urls.entity
        : this.getParent().super.urls.entity || this.getParent().urls.entity;
      if (this.beforeGetEntity) {
        this.beforeGetEntity(params, row, "edit");
      }
      this.formModalVisible = true;
      this.formModalLoading = true;
      this.getParent()
        .getEntity(params, url)
        .then(vm => {
          this.viewModel = vm;
          let primaryKey = this.getPrimaryKey(this.keyProp, row);
          this.primaryKey = primaryKey;
          this.getParent().pageAction = "edit";
          this.formPath = this.computePage("edit");
          this.formModalLoading = false;
          this.$nextTick(() => {
            this.formDisabled = false;
          });
        })
        .catch(e => {
          this.msgError(e || "请求数据失败，请稍后再试");
        });
    },
    excelExport(postfix = ".xlsx") {
      let formMap = "{";
      this.getTableColumns().forEach(col => {
        formMap += `"${col.field}":"${col.title}",`;
      });
      formMap = formMap.substring(0, formMap.length - 1) + "}";

      let search = this.getParent().search;
      let queryParam = "";
      for (let o in search) {
        let queryKey = "query[" + o + "]";
        let queryValue = data.search[o];
        queryParam += `&${encodeURIComponent(queryKey)}=${encodeURIComponent(
          queryValue
        )}`;
      }
      let server = process.env.VUE_APP_SERVER;
      window.location.href = `${server}${
        this.getParent().urls.excelExport
      }?jsonStr=${encodeURIComponent(formMap)}&postfix=${encodeURIComponent(
        postfix
      )}${queryParam}`;
    },
    seniorExport() {
      this.exportModalVisible = true;
      this.exportColumns = this.getTableColumns();
    },
    getTableColumns() {
      let columns = [];
      let defaults = this.$slots.default || [];
      _.each(defaults, vnode => {
        if (vnode.tag && vnode.tag.includes("qm-table-column")) {
          let colComp = vnode.componentInstance;
          let vxeTableCol = colComp.$refs["qmTableColumn"];
          let field = vxeTableCol.field;
          let title =
            vxeTableCol.title ||
            (colComp.$slots.default || [{ text: "" }])[0].text.trim();
          columns.push({ field: field, title: title });
        }
      });
      return columns;
    },

    doValidate(formName) {
      if (this.useFormPage) {
        return this.$refs["formPage"].$refs[formName].validate();
      }
      if (this.isCustomForm) {
        return this.getParent().$refs[formName].validate();
      }
      return this.$refs[formName].validate();
    },
    defaultFormValidate(forms, idx, rs, rj) {
      this.doValidate(forms[idx])
        .then(valid => {
          if (valid) {
            if (forms.length - 1 === idx) {
              rs(true);
            } else {
              this.defaultFormValidate(forms, idx + 1, rs, rj);
            }
          } else {
            rj(false);
          }
        })
        .catch(e => {
          console.error(e);
          this.notifyError("表单检验不通过，请检查表单");
        });
    },
    validate() {
      let formNames = this.formRefs;
      let forms = formNames.split(",");
      if (this.formValidate) {
        return this.formValidate(forms);
      }
      return new Promise((rs, rj) => {
        this.defaultFormValidate(forms, 0, rs, rj);
      });
    },

    submitForm() {
      this.validate()
        .then(valid => {
          if (valid) {
            let postModel = this.viewModel.models;
            let $formPage = this.$refs["formPage"];

            let url = this.isRemoteList
              ? this.urls.save
              : this.getParent().super.urls.save || this.getParent().urls.save;
            url = this.appendPrimaryKey(url);
            let next = true;

            if (this.useFormPage && $formPage && $formPage.onBeforeSave) {
              let go = $formPage.onBeforeSave(postModel);
              // undefined 是没有写return返回值的情况，可能有人忘记写返回值，这里帮忙处理了
              next = go === true || go === undefined;
            }
            if (!next) {
              return;
            }
            this.saveLoading = true;
            this.getParent()
              .save(postModel, url)
              .then(r => {
                let success = (r || {}).success;
                if (this.useFormPage && $formPage && $formPage.onAfterSave) {
                  $formPage.onAfterSave(success, r);
                }
                if (success) {
                  this.formModalVisible = false;
                  this.saveLoading = false;
                  if (this.refreshAfterSave) {
                    this.refresh();
                  }
                } else {
                  this.notifyError((r || {}).message || "操作失败，请稍后再试");
                  this.saveLoading = false;
                }
              })
              .catch(() => {
                this.saveLoading = false;
              });
          } else {
            console.log("error submit!!");
            this.notifyError("表单检验不通过，请检查表单");
            return false;
          }
        })
        .catch(err => {
          console.error(err);
          this.notifyError("表单检验不通过，请检查表单");
        });
    },
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
    appendPrimaryKey(url) {
      if (!url) {
        return "";
      } else {
        for (let o in this.primaryKey) {
          let v = this.primaryKey[o];
          if (v != undefined) {
            let joinChar = url.indexOf("?") == -1 ? "?" : "&";
            url += joinChar;
            url += o + "=" + (this.primaryKey[o] || "");
          }
        }
        return url;
      }
    },
    getPrimaryKey(keys, row) {
      let o = {};
      _.each(keys.split(","), key => {
        let k = key.trim();
        o[k] = row[k];
      });
      return o;
    },
    fetchListData(otherParams = {}) {
      if (!this.isRemoteList) {
        return;
      }
      let url = this.urls.query;
      url = _.isString(url)
        ? { url: url, method: "post" }
        : _.extend({ url: "", method: "post" }, url);

      let params = _.extend(
        {},
        {
          start: 0,
          length: this.finalPagerOptions.pageSize || 10,
          draw: 1,
          query: {}
        },
        otherParams
      );

      if (this.beforeQuery) {
        this.beforeQuery(params);
      }
      let queryData =
        url.method.toLowerCase() === "get"
          ? { params: params }
          : { data: params };
      this.tableLoading = true;
      request({
        url: url.url,
        method: url.method,
        ...queryData
      })
        .then(
          result => {
            if (this.afterQuery) {
              this.afterQuery(true, result);
            }
            this.data = _.extend(this.data, result);
            this.tableLoading = false;
          },
          result => {
            if (this.afterQuery) {
              this.afterQuery(false, result);
            }
            this.tableLoading = false;
            this.msgError("获取列表数据失败，请稍后重试");
          }
        )
        .catch(err => {
          console.error(err);
          this.tableLoading = false;
        });
    },
    showForm() {
      this.formModalVisible = true;
    },
    closeForm() {
      this.formModalVisible = false;
    },
    isTreeExpandByRow(row) {
      return this.$refs["xTable"].isTreeExpandByRow(row);
    },
    getSelectedRows() {
      if (this.showCheckbox) {
        return this.$refs["xTable"].getCheckboxRecords();
      }
      if (this.showRadio) {
        return this.$refs["xTable"].getRadioRecord();
      }
      return [];
    },
    getRowById(rowId) {
      return this.$refs["xTable"].getRowById(rowId);
    },
    getModels() {
      return this.viewModel.models;
    },
    /**
     * 清空指定列的排序条件
     * 如果为空则清空所有列的排序条件
     * @param {String} fieldOrColumn 列或字段名
     */
    clearSort(fieldOrColumn) {
      return this.$refs["xTable"].clearSort(fieldOrColumn);
    },
    excuteEvent(key,options){
      if(this.eventOptions[key]){
        this.eventOptions[key](options);
      }
    }
  },
  created() {
    if (!this.isRemoteList) {
      return;
    }
    this.fetchListData();
  },
  watch: {
    urls(n, o) {
      if (!this.isRemoteList) {
        return;
      }
      let q1 = (n || {}).query;
      let q2 = (o || {}).query;
      if (!q1 || q1 === q2) {
        return;
      }
      this.fetchListData();
    }
  }
};
</script>

<style scoped lang="scss">
.qm-table {
  margin: 12px 0;

  .qm-table-vxe-table {
    margin: 6px 0;

    .qm-table-menu-link {
      padding-right: 8px;
    }
  }
  .qm-table-form-modal {
    .qm-table-form {
      max-height: calc(100vh - 200px);
      margin: 8px 16px 8px 8px;
      .qm-table-form-toolbar {
        text-align: right;
      }
    }
    .qm-table-form-row {
      text-align: right;
      // margin: 12px 0;
    }
  }
  .qm-table-el-pager {
    margin-top: 16px;
    font-weight: normal;
  }
}
</style>
